﻿#include "stdafx.h"

struct TaskbarAntigravity
{
    HWND wnd;

    int run(HINSTANCE hInst)
    {
        try
        {
            WNDCLASS wc;
            ZeroMemory(&wc, sizeof(wc));
            wc.lpfnWndProc      = s_windowProc;
            wc.hInstance        = hInst;
            wc.lpszClassName    = L"TaskbarAntigravityService";
            ATOM wndClass = RegisterClass(&wc);

            wnd = CreateWindow(L"TaskbarAntigravityService", L"", 0, 0, 0, 0, 0, 0, 0, hInst, this);
            DWORD le = GetLastError();

            RegisterHotKey(0, DMDO_DEFAULT, MOD_CONTROL | MOD_WIN | MOD_NOREPEAT, VK_UP);
            RegisterHotKey(0, DMDO_270, MOD_CONTROL | MOD_WIN | MOD_NOREPEAT, VK_RIGHT);
            RegisterHotKey(0, DMDO_180, MOD_CONTROL | MOD_WIN | MOD_NOREPEAT, VK_DOWN);
            RegisterHotKey(0, DMDO_90, MOD_CONTROL | MOD_WIN | MOD_NOREPEAT, VK_LEFT);

            MSG msg;
            while (GetMessage(&msg, 0, 0, 0))
            {
                DispatchMessage(&msg);

                if (msg.message == WM_HOTKEY)
                {
                    DEVMODE dm;
                    ZeroMemory(&dm, sizeof(dm));
                    dm.dmSize = sizeof(dm);

                    if (0 != EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dm))
                    {
                        bool realChange = (isVertical(dm.dmDisplayOrientation) != isVertical(msg.wParam));
                        if (realChange)
                        {
                            // swap height and width
                            DWORD dwTemp = dm.dmPelsHeight;
                            dm.dmPelsHeight= dm.dmPelsWidth;
                            dm.dmPelsWidth = dwTemp;
                        }

                        dm.dmDisplayOrientation = msg.wParam;
                        ChangeDisplaySettings(&dm, CDS_RESET);

                        // in case taskbar positioning code failed, user will want to press the hotkey again to repeat
                        // WM_DISPLAYCHANGE will not arrive in this case
                        if (!realChange)
                            repositionTaskbar();
                    }
                }
            }
        }
        catch (...)
        {
            return 1;
        }
        return 0;
    }

    bool isVertical(int orientation)
    {
        return orientation == DMDO_90 || orientation == DMDO_270;
    }

    void moveTaskbar(bool up)
    {
        HWND fgWnd = GetForegroundWindow();
        POINT cursorPos;
        GetCursorPos(&cursorPos);

        HWND taskBar = FindWindow(L"Shell_TrayWnd", 0);
        SetForegroundWindow(taskBar);

        // When window keyboard-move mode starts, mouse cursor jumps near top-left corner of the window.
        // So, move mouse to the center of the screen and wait until it jumps from there.
        int w = GetSystemMetrics(SM_CXSCREEN);
        int h = GetSystemMetrics(SM_CYSCREEN);

        moveMouseTo(w/2, h/2);
        selectMoveCommand();

        // if the taskbar is locked, move mode will not start
        bool wasLocked = false;
        if (!waitUntilMouseNotAt(w/2, h/2))
        {
            SendMessage(taskBar, WM_COMMAND, 424, 0); // unlock the taskbar
            wasLocked = true;
            selectMoveCommand();
            waitUntilMouseNotAt(w/2, h/2);
        }

        POINT pt = { (up ? w/2 : 8), (up ? 8 : h/2) };
        moveMouseTo(pt.x, pt.y);

        pressKey(VK_RETURN); releaseKey(VK_RETURN); // accept the new position
        waitUntilMouseNotAt(pt.x, pt.y);

        if (wasLocked) SendMessage(taskBar, WM_COMMAND, 424, 1);
        SetCursorPos(cursorPos.x, cursorPos.y);
        SetForegroundWindow(fgWnd);
    }

    void selectMoveCommand()
    {
        pressKey(VK_LMENU); pressKey(VK_SPACE); releaseKey(VK_SPACE); releaseKey(VK_LMENU); // alt+space to open window menu
        pressKey(VK_DOWN); releaseKey(VK_DOWN); pressKey(VK_DOWN); releaseKey(VK_DOWN); pressKey(VK_RETURN); releaseKey(VK_RETURN); // select Move command
        pressKey(VK_RIGHT); releaseKey(VK_RIGHT); // right arrow to enter keyboard-move mode
    }

    void pressKey(int vk) 
    { 
        return keyEvent(vk, 0); 
    }

    void releaseKey(int vk) 
    { 
        return keyEvent(vk, KEYEVENTF_KEYUP); 
    }

    void keyEvent(int vk, int flags)
    {
        INPUT inp;
        ZeroMemory(&inp, sizeof(inp));
        inp.type = INPUT_KEYBOARD;
        inp.ki.wVk = vk;
        inp.ki.dwFlags = flags;
        SendInput(1, &inp, sizeof(INPUT));
    }

    bool moveMouseTo(int x, int y)
    {
        DWORD start = GetTickCount();
        while (GetTickCount() - start < 2000)
        {
            mouseEvent(x, y, MOUSEEVENTF_MOVE);

            POINT cp;
            GetCursorPos(&cp);

            if (abs(cp.x - x) < 8 && abs(cp.y - y) < 8)
                return true;

            Sleep(10);
        }
        return false;
    }

    void mouseEvent(int x, int y, int flags)
    {
        INPUT inp;
        ZeroMemory(&inp, sizeof(inp));
        inp.type = INPUT_MOUSE;
        inp.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | flags;
        inp.mi.dx = x * 65535 / GetSystemMetrics(SM_CXSCREEN);
        inp.mi.dy = y * 65535 / GetSystemMetrics(SM_CYSCREEN);
        SendInput(1, &inp, sizeof(INPUT));
    }

    bool waitUntilMouseNotAt(int x, int y)
    {
        DWORD start = GetTickCount();
        while (GetTickCount() - start < 2000)
        {
            POINT cp;
            GetCursorPos(&cp);

            if (!( (abs(cp.x - x) < 8 && abs(cp.y - y) < 8) ))
                return true;

            Sleep(10);
        }
        return false;
    }

    static LRESULT CALLBACK s_windowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_NCCREATE)
        {
            CREATESTRUCT& cs = *(CREATESTRUCT*)lParam;
            SetWindowLongPtr(hwnd, GWL_USERDATA, LONG_PTR(cs.lpCreateParams));
        }

        TaskbarAntigravity* self = (TaskbarAntigravity*)GetWindowLongPtr(hwnd, GWL_USERDATA);
        if (self)
        {
            try
            {
                return self->windowProc(uMsg, wParam, lParam);
            }
            catch (...)
            {
            }
        }
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    LRESULT windowProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if (uMsg == WM_NCCREATE)
            return TRUE;
        if (uMsg == WM_DISPLAYCHANGE)
        {
            Sleep(500);
            repositionTaskbar();
        }
        return DefWindowProc(wnd, uMsg, wParam, lParam);
    }

    void repositionTaskbar()
    {
        releaseKey(VK_LCONTROL); releaseKey(VK_LWIN);
        releaseKey(VK_RCONTROL); releaseKey(VK_RWIN);
        releaseKey(VK_UP); releaseKey(VK_LEFT); releaseKey(VK_DOWN); releaseKey(VK_RIGHT);
        moveTaskbar(GetSystemMetrics(SM_CXSCREEN) < GetSystemMetrics(SM_CYSCREEN));
    }
};



int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    return TaskbarAntigravity().run(hInstance);
}
